package model.trajectory;

import java.util.ArrayList;
import java.util.Date;
import java.util.Iterator;

import ai.PathFollowingBehaviour;

import model.BaseModel;
import model.DoorModel;
import model.MainModel;
import model.RoomModel;
import model.path.Path;
import model.path.Point4D;
import colors.Color;
import utils.Pair;
import utils.Point3D;
import utils.TimeIntervalle;

public class TrajectoryModel extends BaseModel
{
	private Color color;
	
	public ArrayList<Pair<TimeIntervalle, BaseTrajectoryModel>> subTrajectories;
	
	public long minTime = -1;
	public long maxTime = 0;
	
	private boolean isVisible = false;
	
	private ArrayList<Point4D> positions;
	
	public TrajectoryModel(int ID)
	{
		super(ID);
		this.subTrajectories = new ArrayList<Pair<TimeIntervalle, BaseTrajectoryModel>>();
		this.setColor(new Color(0.0f, 0.0f, 0.0f));
	}
	
	public TrajectoryModel(int ID, MainModel model, ArrayList<Point4D> positions)
	{
		this(ID);
		this.positions = positions;
		initialize(model, positions);
	}
	
	
	public TrajectoryModel(int ID, MainModel model, Path path, long beginTime)
	{
		this(ID);
		this.positions = PathFollowingBehaviour.generateBehaviour(path, beginTime);
		initialize(model, positions);
	}

	private void initialize(MainModel model, ArrayList<Point4D> positions)
	{
		ArrayList<ArrayList<Point4D>> subPath = new ArrayList<ArrayList<Point4D>>();
		subPath.add(new ArrayList<Point4D>());
		int index = 0;
		RoomModel lastRoom = null;
		
		for(Iterator<Point4D> it = positions.iterator(); it.hasNext();)
		{
			Point4D currentPoint = it.next();

			subPath.get(index).add(currentPoint);	
			if(lastRoom != null && lastRoom != currentPoint.getRoom())
			{
				index++;
				subPath.add(new ArrayList<Point4D>());
			}
			lastRoom = currentPoint.getRoom();
		}	
		

		lastRoom = null;
		for(int i = 0; i < subPath.size(); i++)
		{
			ArrayList<Point4D> list = subPath.get(i);
			RoomModel currentRoom = list.get(0).getRoom();
			long subPathBeginTime;
			long endTime;
			
			if(i == subPath.size()-1)
			{
				subPathBeginTime = list.get(0).getTime().getTime() + 5;
				endTime = list.get(list.size()-1).getTime().getTime();
			}
			else
			{
				subPathBeginTime = list.get(0).getTime().getTime() + 5;
				endTime = subPath.get(i+1).get(0).getTime().getTime();
			}
			
			if(this.minTime == -1)
			{
				this.minTime = subPathBeginTime;
			}
			if(subPathBeginTime <= minTime)
			{
				minTime = subPathBeginTime;
			}
			
			if(endTime >= maxTime)
			{
				maxTime = endTime;
			}
			
			TimeIntervalle intervalle = new TimeIntervalle(subPathBeginTime, endTime );
			RoomTrajectoryModel roomTrajModel = new RoomTrajectoryModel(this, intervalle, list, currentRoom);
			this.subTrajectories.add(new Pair<TimeIntervalle, BaseTrajectoryModel>(intervalle, roomTrajModel));		
			
			Point3D lastPoint = list.get(list.size()-1);
			
			if(i < subPath.size() - 1)
			{
				subPath.get(0);
				ArrayList<DoorModel> doors = model.getDoors(subPath.get(i).get(0).getRoom(), subPath.get(i+1).get(0).getRoom());
				DoorModel door = null;
				if(doors.size() > 1)
				{
					long length = 999999999;
					for(Iterator<DoorModel> itDoor = doors.iterator(); itDoor.hasNext();)
					{
						DoorModel currentDoor = itDoor.next();
						Point3D doorPosition = currentDoor.AnchoragePoint;
						double intermediateLength = Point3D.diff(doorPosition, lastPoint).lengthSquared();
						if(length >= intermediateLength)
						{
							door = currentDoor;
							length = (long) intermediateLength;
						}
					}
				}
				else
				{
					door = doors.get(0);
				}
				ArrayList<Point4D> transitionPoints = new ArrayList<Point4D>();
				transitionPoints.add(new Point4D(currentRoom.position,  currentRoom,  new Date(++endTime)));
				transitionPoints.add(new Point4D(door.AnchoragePoint,  currentRoom, new Date(++endTime)));
				
				boolean reverse;
				if(door.FirstRoom == currentRoom)
				{
					reverse = false;
					transitionPoints.add(new Point4D(door.SecondRoom.position, door.SecondRoom, new Date(++endTime)));
				}
				else
				{
					reverse = true;
					transitionPoints.add(new Point4D(door.FirstRoom.position, door.FirstRoom, new Date(++endTime)));
				}
					
				TimeIntervalle transitionIntervalle = new TimeIntervalle(transitionPoints.get(0).getTime().getTime(), transitionPoints.get(transitionPoints.size()-1).getTime().getTime());
				TransitionTrajectoryModel transitionTrajModel = new TransitionTrajectoryModel(this, transitionIntervalle, transitionPoints, door, reverse);
				this.subTrajectories.add(new Pair<TimeIntervalle, BaseTrajectoryModel>(transitionIntervalle, transitionTrajModel));
			}
		}
	}
	
	@Override
	public String toString()
	{
		String returnValue = "";
		
		for(Iterator<Point4D> it = this.positions.iterator(); it.hasNext();)
		{
			returnValue += it.next() + "\n";
		}
		return returnValue;
	}
	
	public RoomModel getRoom(TimeIntervalle intervalle)
	{
		for(Iterator<Pair<TimeIntervalle, BaseTrajectoryModel>> it = this.subTrajectories.iterator(); it.hasNext();)
		{
			Pair<TimeIntervalle, BaseTrajectoryModel> pair = it.next();

			BaseTrajectoryModel model = pair.value;
			TimeIntervalle currentIntervalle = pair.key;
			
			if(currentIntervalle.contains(intervalle))
			{
				if(model.getClass() == RoomTrajectoryModel.class )
				{
					return ((RoomTrajectoryModel)model).room;
				}
			}
		}
		return null;
	}

	public DoorModel getTransition(TimeIntervalle intervalle) 
	{
		for(Iterator<Pair<TimeIntervalle, BaseTrajectoryModel>> it = this.subTrajectories.iterator(); it.hasNext();)
		{
			Pair<TimeIntervalle, BaseTrajectoryModel> pair = it.next();

			BaseTrajectoryModel model = pair.value;
			TimeIntervalle currentIntervalle = pair.key;
			
			if(currentIntervalle.contains(intervalle))
			{
				if(model.getClass() == TransitionTrajectoryModel.class )
				{
					return ((TransitionTrajectoryModel)model).door; 
				}
			}
		}
		return null;
	}

	public String saveToText() 
	{
		String text = "";
		for(Iterator<Pair<TimeIntervalle, BaseTrajectoryModel>> it = this.subTrajectories.iterator(); it.hasNext();)
		{
			Pair<TimeIntervalle, BaseTrajectoryModel> pair = it.next();

			BaseTrajectoryModel model = pair.value;
			
			text += model.toSaveText();	
		}
		return text;
	}

	public Color getColor() {
		return color;
	}

	public void setColor(Color color) {
		for(Iterator<Pair<TimeIntervalle, BaseTrajectoryModel>> it = this.subTrajectories.iterator(); it.hasNext();)
		{
			it.next().value.setColor(color);
		}
		this.color = color;
	}

	public boolean isVisible() {
		// TODO Auto-generated method stub
		return this.isVisible;
	}

	public void setVisible(boolean isVisible) {
		this.isVisible = isVisible;
	}
}
